/**
 * @file rtp part of sipxmppserver. with ice support
 *
 * */
#include "ice_rtp.h"
#include <errno.h>
extern int errno;
#define UDP_BUFFER_SIZE 4196
#define REQUESTDATALEN 512
#define STUN_HEADER_LEN 20
#define STUN_HMAC_LEN 20

/**
 * set ice username  to stun package.
 * @param buffer the buffer of stun package.
 * @param username  username to set.
 * @param username_len length of username. normaly, use strlen(username)
 * @return set result. USERNAME len.
 * */
static int ice_rtp_set_username(char *buffer,char *username,int username_len)
{
	*(short *)buffer = HTONS(ICE_RTP_ATTRIBUTE_USERNAME);
	*((short *)buffer + 1) = HTONS(username_len);
	memcpy(buffer + 4,username,username_len);
	return 4 + username_len;
}
static int ice_rtp_set_password(char *buffer,char *password,int password_len)
{
	*(short *)buffer = HTONS(ICE_RTP_ATTRIBUTE_PASSWORD);
	*((short *)buffer + 1) = HTONS(password_len);
	memcpy(buffer + 4,password,password_len);
	return 4 + password_len;
}
static void ice_rtp_send_dtmf(struct ice_rtp_connection *rtp_connection,char digit)
{
	char seq_data[16];
	*((short *)(seq_data)) = HTONS(0x8065);
	rtp_connection->rtp_package_sequence++;
	*((short *)(seq_data + 2)) = HTONS(rtp_connection->rtp_package_sequence);
	*((unsigned int *)(seq_data + 4)) = HTONL(rtp_connection->rtp_package_timespan);
	*((unsigned int *)(seq_data + 8)) = HTONL(rtp_connection->rtp_package_ssrc);
	*(seq_data + 12) = digit;
	*(seq_data + 13) = 0x8A;
	*((unsigned short *)(seq_data + 14)) = HTONS(0x0140);
	LogD("sending dtmf [%d]",digit);
	sendto(rtp_connection->fd, seq_data, 16, 0, (const struct sockaddr *)&rtp_connection->serv_addr, sizeof(rtp_connection->serv_addr));
	rtp_connection->rtp_package_timespan += 0x0140;
}
/**
 * send stun active package if needed. we send active every 30 seconds.
 * @param rtp_connection the rtp connection
 * @param buffer	the buffer to use.
 * */
static void test_and_send_ice(struct ice_rtp_connection *rtp_connection, char *buffer)
{
	if(rtp_connection->last_negotiation_time && time(0) - rtp_connection->last_negotiation_time > 30)
	{
		char r_l_username[128];
		char *requestdata_tmp = buffer;
		snprintf(r_l_username,128,"%s%s",rtp_connection->remote_username,rtp_connection->local_username);
		LogD("r_l_username=[%s],remote_password=%s",r_l_username,rtp_connection->remote_password);
		*((short*)requestdata_tmp) = HTONS(ICE_RTP_METHOD_BINDING_REQUEST);
		requestdata_tmp += 2;
		//body len
		requestdata_tmp += 2;
		//transaction_id
		sipxmpp_createrandomdata(requestdata_tmp,16);
		requestdata_tmp += 16;

		requestdata_tmp += ice_rtp_set_username(requestdata_tmp,r_l_username,strlen(r_l_username));
		if(rtp_connection->remote_password && rtp_connection->remote_password[0])
			requestdata_tmp += ice_rtp_set_password(requestdata_tmp,rtp_connection->remote_password,strlen(rtp_connection->remote_password));

		*((short*)(buffer + 2)) = HTONS(requestdata_tmp - buffer - STUN_HEADER_LEN);


		if(sendto(rtp_connection->fd, buffer, requestdata_tmp - buffer, 0, (const struct sockaddr *)&rtp_connection->serv_addr, sizeof(rtp_connection->serv_addr)) <=0)
		{
			LogE("send error,errno=0x%x,fd=%d,error=%s",errno,rtp_connection->fd,strerror(errno));
		}
		rtp_connection->last_negotiation_time = time(0) - 29;
	}
}
/**
 * reading thread function.
 * */
static void *ice_rtp_thread_reading(void *obj)
{
	struct ice_rtp_connection *connection = (struct ice_rtp_connection *)obj;
	char *remotedata = sipxmpp_calloc(UDP_BUFFER_SIZE);
	short remote_body_len = 0;
	char *remotedata_tmp = NULL;
	char *remote_transactionid = NULL;
	int received_count;
	struct sockaddr_in remote_addr;
	socklen_t remote_addr_len;
	char check_username_ok;
	struct rtp_t rtp_packet;
	struct ice_stun_message_attribute *tmp_attribute;
	clock_t start;
	struct ice_stun_message_attribute *attributes = NULL;
	fd_set	rtp_receivefd_set;
	struct timeval	tval;
	unsigned short extension_header_len;

	while(!connection->stopping)
	{
		test_and_send_ice(connection,remotedata);
		FD_ZERO(&rtp_receivefd_set);
		tval.tv_sec = 1L;
		tval.tv_usec = 500L;
		FD_SET(connection->fd,&rtp_receivefd_set);
		if(!select(FD_SETSIZE, &rtp_receivefd_set, NULL, NULL, &tval) || !FD_ISSET(connection->fd,&rtp_receivefd_set))
		{
			if(connection->dtmf_to_send_if_no_data)
			{
				LogD("try send dtmf.");
				ice_rtp_send_dtmf(connection,connection->dtmf_to_send_if_no_data);
			}
			continue;
		}
		if((received_count = recvfrom(connection->fd,remotedata,UDP_BUFFER_SIZE,0,(struct sockaddr *)&remote_addr,&remote_addr_len)) <= 0)
		{
			Log("rtp receive error number=[%d],error=[%s]",errno,strerror(errno));
			continue;
		}
		remotedata_tmp = remotedata;
		if(*remotedata_tmp & 0x80)
		{
			rtp_packet.version = (*remotedata_tmp >> 6 & 0x3);
			rtp_packet.padding = (*remotedata_tmp >> 5 & 0x1);
			rtp_packet.extension = (*remotedata_tmp >> 4 & 0x1);
			rtp_packet.csrc_count = (*remotedata_tmp & 0xF);
			remotedata_tmp +=1;
			rtp_packet.marker = (*remotedata_tmp >> 7 & 0xF);
			rtp_packet.payload_type = (*remotedata_tmp & 0x7F);
			remotedata_tmp +=1;

			rtp_packet.sequence = HTONS(*(short*)remotedata_tmp);

			remotedata_tmp += 2;
			rtp_packet.timestamp = HTONL(*(int*)remotedata_tmp);
			if(connection->bridged_rtp)
			{
				connection->bridged_rtp->rtp_package_sequence = rtp_packet.sequence;
				connection->bridged_rtp->rtp_package_timespan = rtp_packet.timestamp;
			}
			remotedata_tmp += 4;
			rtp_packet.ssrc = HTONL(*(int*)remotedata_tmp);
			*((int *)remotedata_tmp) = NTOHL(connection->bridged_rtp->rtp_package_ssrc);
			if(connection->bridged_rtp)
				sipxmpp_debug(SIPXMPP_DEBUG_LEVEL_LOWEST,"receive_count=%u:rtp_packet.sequence=[%u],sequence=[%u],timespan=[%u],rtp.timespan=[%u],ssrc=[%u],rtp.ssrc=[%u],payload=[%d],csrc_count=[%d]",
						received_count, rtp_packet.sequence,connection->bridged_rtp->rtp_package_sequence,connection->bridged_rtp->rtp_package_timespan,rtp_packet.timestamp,
						connection->bridged_rtp->rtp_package_ssrc,rtp_packet.ssrc,rtp_packet.payload_type,rtp_packet.csrc_count);

			remotedata_tmp += 4;

			rtp_packet.csrc = remotedata_tmp;
			if(rtp_packet.csrc_count)
			{
				remotedata_tmp += rtp_packet.csrc_count;
				Log("csrc ignored.");
			}

			if(rtp_packet.extension)
			{
				rtp_packet.extension_header = remotedata_tmp;
				remotedata_tmp += 2;//defined by profile;
				extension_header_len = HTONS(*(short*)remotedata_tmp);
				remotedata_tmp += 2;
				remotedata_tmp += extension_header_len;//skip the extension header
				Log("extension_header ignored.");
			}
			if(connection->bridged_rtp && rtp_packet.version == 2 && rtp_packet.payload_type == connection->payload_type_pcmu) //pcmu
			{
				if(connection->bridged_rtp && sendto(connection->bridged_rtp->fd, remotedata, received_count, 0, (const struct sockaddr *)&connection->bridged_rtp->serv_addr, sizeof(connection->bridged_rtp->serv_addr)) <=0)
				{
					char sendip[30];
					inet_ntop(AF_INET, &(connection->bridged_rtp->serv_addr.sin_addr), sendip, 30);
					LogE("send error,errno=0x%x,fd=%d,address=[%s]",errno,connection->bridged_rtp->fd,sendip);
				}

			}
		}
		else //stun package
		{
			char l_r_username[128],r_l_username[128];
			enum ICE_RTP_METHOD remote_message_method = NTOHS(*(short*)remotedata_tmp);
			remotedata_tmp += 2;
			remote_body_len = NTOHS(*(short*)remotedata_tmp);
			remotedata_tmp += 2;
			remote_transactionid = remotedata_tmp;
			remotedata_tmp += 16;
			
			//body
			while(remotedata_tmp < remotedata + received_count)
			{
				if(attributes)
				{
					tmp_attribute = attributes;
					while(tmp_attribute->next)
					{
						tmp_attribute = tmp_attribute->next;
					}
					tmp_attribute->next = sipxmpp_calloc(sizeof(struct ice_stun_message_attribute));
					tmp_attribute = tmp_attribute->next;
				}
				else
				{
					attributes = sipxmpp_calloc(sizeof(struct ice_stun_message_attribute));
					tmp_attribute = attributes;
				}
				
				memset(tmp_attribute,0,sizeof(struct ice_stun_message_attribute));

				tmp_attribute->type =  NTOHS(*(short*)remotedata_tmp);
				remotedata_tmp += 2;
				tmp_attribute->len = NTOHS(*(short*)remotedata_tmp);
				remotedata_tmp += 2;
				tmp_attribute->data = remotedata_tmp;
				remotedata_tmp += tmp_attribute->len;
			}
			//body done.
			switch(remote_message_method)
			{
				case ICE_RTP_METHOD_BINDING_REQUEST:
					snprintf(l_r_username,128,"%s%s",connection->local_username,connection->remote_username);
					tmp_attribute = attributes;
					check_username_ok = 0;
					while(!check_username_ok && tmp_attribute)
					{
						if(tmp_attribute->type == ICE_RTP_ATTRIBUTE_USERNAME)
						{
							if(!memcmp(tmp_attribute->data,l_r_username,MIN(strlen(l_r_username),tmp_attribute->len)))
							{
								check_username_ok = 1;
							}
						}
						else
						{
							Log("request,attribute type=0x%x",tmp_attribute->type);
						}
						tmp_attribute = tmp_attribute->next;
					}
					if(check_username_ok)
					{
						//response correct
						char *requestdata = sipxmpp_calloc(REQUESTDATALEN);
						char *requestdata_tmp = requestdata;
						memset(requestdata,0,REQUESTDATALEN);
						
						*((short*)requestdata_tmp) = HTONS(ICE_RTP_METHOD_BINDING_RESPONSE);
						requestdata_tmp += 2;
						//bodylen
						requestdata_tmp += 2;
						//transactionid
						memcpy(requestdata_tmp,remote_transactionid,16);
						requestdata_tmp += 16;
						//username
						requestdata_tmp += ice_rtp_set_username(requestdata_tmp,l_r_username,strlen(l_r_username));
						//mapped ip
						*((short *)requestdata_tmp) = HTONS(ICE_RTP_ATTRIBUTE_MAPPED_ADDRESS);
						requestdata_tmp += 2;
						*((short *)requestdata_tmp) = HTONS(0x0008);//len
						requestdata_tmp += 2;
						
						*((short *)requestdata_tmp) = HTONS(0x0001);//family
						requestdata_tmp+=2;
						*((short *)requestdata_tmp) = remote_addr.sin_port;//port
						requestdata_tmp+=2;
						*((struct in_addr *)requestdata_tmp) = remote_addr.sin_addr;
						requestdata_tmp+=4;

						*((short *)(requestdata + 2)) = HTONS(requestdata_tmp - requestdata - STUN_HEADER_LEN);

						if(sendto(connection->fd, requestdata, requestdata_tmp - requestdata, 0, (const struct sockaddr *)&connection->serv_addr, sizeof(connection->serv_addr)) <=0)
						{
							Log("send error,errno=0x%x,fd=%d",errno,connection->fd);
						}
						chkndfree(requestdata);
					}
					break;
				case ICE_RTP_METHOD_BINDING_RESPONSE:
					snprintf(r_l_username,128,"%s%s",connection->remote_username,connection->local_username);
					LogD("ICE_RTP_METHOD_BINDING_RESPONSE");
					tmp_attribute = attributes;
					check_username_ok = 0;
					while(tmp_attribute)
					{
						if(tmp_attribute->type == ICE_RTP_ATTRIBUTE_USERNAME)
						{
							if(!memcmp(tmp_attribute->data,r_l_username,MIN(strlen(r_l_username),tmp_attribute->len)))
							{
								//error. user name incorrect.
								check_username_ok = 1;
							}
							else
							{
								LogD("check_user_name is not ok. our[%s],their[%.*s]",r_l_username,tmp_attribute->len,tmp_attribute->data);
							}
						}
						if(check_username_ok && tmp_attribute->type == ICE_RTP_ATTRIBUTE_MAPPED_ADDRESS)
						{
							char externalip[20];
							int externalip_len = 20;
							LogD("family=0x%x",tmp_attribute->data[1]);
							LogD("port=%d",NTOHS(*((short*)(tmp_attribute->data + 2))));
							if(connection->dtmf_to_send_after_ice_success)
							{
								ice_rtp_send_dtmf(connection,connection->dtmf_to_send_after_ice_success);
								connection->dtmf_to_send_after_ice_success = 0;
							}
							if(!inet_ntop(AF_INET,tmp_attribute->data + 4,externalip,externalip_len))
							{
								LogE("get addr failed.");
							}
							else
							{
								LogD("address=%s",externalip);
							}
						}
						tmp_attribute = tmp_attribute->next;
					}
					
					time(&connection->last_negotiation_time);
					
					break;
				case ICE_RTP_METHOD_BINDING_ERROR_RESPONSE:
					tmp_attribute = attributes;
					check_username_ok = 1;
					while(tmp_attribute)
					{
						if(tmp_attribute->type == ICE_RTP_ATTRIBUTE_ERROR_CODE)
						{
							Log("error class=0x%d,number=%d,reason=%.*s.remote=[%s]",tmp_attribute->data[2],tmp_attribute->data[3],tmp_attribute->len - 4,tmp_attribute->data + 4
							,inet_ntoa(remote_addr.sin_addr)
							);
						}
						else
						{
							Log("error attribute type=0x%x",tmp_attribute->type);
						}
						tmp_attribute = tmp_attribute->next;
					}
					break;
				default:
					break;
			}
			//free attributes
			{
				while(attributes)
				{
					tmp_attribute = attributes->next;
					chkndfree(attributes);
					attributes = tmp_attribute;				
				}
				attributes = NULL;
			}
		}
	}
	
	chkndfree(remotedata);
	return 0;
}
/**
 * get ip address by send message to stun server.
 * @param dgram_fd			udp socket fd.
 * @param stun_server		the stun server
 * @param stun_server_port	stun server port.
 * @param nated_address		result ipaddress.
 * @param nated_address_len	ipaddress length.
 * @param nated_port		udp socket port.
 * @return result. 0/success. 1/failed.
 * */
int stun_request_ip(int dgram_fd,char *stun_server,int stun_server_port,char *nated_address,int nated_address_len,int *nated_port)
{
	fd_set						stun_receivefd_set;
	struct timeval				tval;
	struct sockaddr_in local_addr,serv_addr;
	struct hostent *server;
	int addr_len;
	unsigned int serv_addr_len;
	short remote_body_len;
	int received_count;
	char requestdata[REQUESTDATALEN],*requestdata_tmp;
	struct ice_stun_message_attribute response_attribute;
	if(!(server = gethostbyname(stun_server))){Log("stun error.errorno=%d,error=%s",errno,strerror(errno));return 1;}
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = HTONS(stun_server_port);
	serv_addr_len=sizeof(struct sockaddr_in);
  	bcopy((char *)server->h_addr, (char *)&(serv_addr.sin_addr),server->h_length);
	
	memset(&local_addr,0,sizeof(local_addr));
	local_addr.sin_family=AF_INET;
	local_addr.sin_addr.s_addr=HTONL(INADDR_ANY);
	local_addr.sin_port= 0;
	addr_len = sizeof(local_addr);
	requestdata_tmp = requestdata;
	memset(requestdata,0,REQUESTDATALEN);
	*((short*)requestdata_tmp) = HTONS(ICE_RTP_METHOD_BINDING_REQUEST);
	requestdata_tmp += 2;
	//body len
	*((short*)(requestdata + 2)) = HTONS(0);
	requestdata_tmp += 2;
	//transaction_id
	sipxmpp_createrandomdata(requestdata_tmp,16);
	requestdata_tmp += 16;
	
	if(sendto(dgram_fd, requestdata, requestdata_tmp - requestdata, 0, (const struct sockaddr *)&serv_addr, serv_addr_len) <=0){
		Log("stun error.errorno=%d,error=%s",errno,strerror(errno));
		return 1;
	}
	
	FD_ZERO(&stun_receivefd_set);
	tval.tv_sec = 1L;
	tval.tv_usec = 0L;
	FD_SET(dgram_fd,&stun_receivefd_set);
	if(select(FD_SETSIZE, &stun_receivefd_set, NULL, NULL, &tval) && FD_ISSET(dgram_fd,&stun_receivefd_set) && (received_count = recvfrom(dgram_fd,requestdata,REQUESTDATALEN,0,(struct sockaddr *)&serv_addr,&serv_addr_len)) > 0)
	{
		requestdata_tmp = requestdata;
		enum ICE_RTP_METHOD remote_message_method = NTOHS(*(short*)requestdata_tmp);
		requestdata_tmp += 2;
		if(remote_message_method == ICE_RTP_METHOD_BINDING_RESPONSE)
		{
			remote_body_len = NTOHS(*(short*)requestdata_tmp);
			requestdata_tmp += 2;
			//remote_transactionid = remotedata_tmp;
			requestdata_tmp += 16;
			while(requestdata_tmp < requestdata + STUN_HEADER_LEN + remote_body_len)
			{
				response_attribute.type =  NTOHS(*(short*)requestdata_tmp);
				requestdata_tmp += 2;
				response_attribute.len = NTOHS(*(short*)requestdata_tmp);
				requestdata_tmp += 2;
				response_attribute.data = requestdata_tmp;
				requestdata_tmp += response_attribute.len;
				
				if(response_attribute.type == ICE_RTP_ATTRIBUTE_MAPPED_ADDRESS)
				{

					*nated_port = NTOHS(*((short*)(response_attribute.data + 2)));
					if(!inet_ntop(AF_INET,response_attribute.data + 4,nated_address,nated_address_len))
					{
						return 1;
					}
					else
					{
						return 0;
					}
				}
			}
		}
		return 1;
	}
	else
	{
		Log("server no response");
		return 1;
	}
}
int ice_rtp_get_local_port(struct ice_rtp_connection *rtp_connection,int *localport)
{
	struct sockaddr_in local_addr;
	int addr_len = sizeof(local_addr);
	if(getsockname(rtp_connection->fd,(struct sockaddr *)&local_addr,&addr_len)) return 1;
	memcpy(localport,&local_addr.sin_port,2);
	*localport = HTONS(*localport);
	return 0;
}

int glue_rtp_together(struct ice_rtp_connection *rtp1,struct ice_rtp_connection *rtp2)
{
	rtp1->bridged_rtp = rtp2;
	rtp2->bridged_rtp = rtp1;
	if(pthread_create(&rtp1->reading_thread,NULL,ice_rtp_thread_reading,rtp1))
	{
		LogE("create thread error");
		return S_ERROR;
	}
	if(pthread_create(&rtp2->reading_thread,NULL,ice_rtp_thread_reading,rtp2))
	{
		LogE("create thread error");
		return S_ERROR;
	}
	return S_OK;
}
struct ice_rtp_connection *create_new_rtp_connection(int *port)
{
	
	struct sockaddr_in local_addr;
	struct ice_rtp_connection *rtp_connection = NULL;
	struct ice_rtp_connection *tmp;
	int addr_len;
	
	rtp_connection = sipxmpp_calloc(sizeof(struct ice_rtp_connection));
	rtp_connection->fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(!rtp_connection->fd) goto error;
	memset(&local_addr,0,sizeof(local_addr));
	local_addr.sin_family=AF_INET;
	local_addr.sin_addr.s_addr=HTONL(INADDR_ANY);
	local_addr.sin_port= 0;
	addr_len = sizeof(local_addr);
	if (bind(rtp_connection->fd,(struct sockaddr *)&local_addr,sizeof(local_addr))<0){Log("error=%d",errno); goto error;}
	if(port)
	{
		ice_rtp_get_local_port(rtp_connection,port);
	}
	sipxmpp_createrandomdata((char *)&rtp_connection->rtp_package_ssrc,4);
	sipxmpp_createrandomdata((char *)&rtp_connection->rtp_package_sequence,2);
	sipxmpp_createrandomdata((char *)&rtp_connection->rtp_package_timespan,4);

	rtp_connection->payload_type_pcmu = 0;
	rtp_connection->payload_type_telephone_event = 101;
	return rtp_connection;
	error:
		if(rtp_connection) chkndfree(rtp_connection);
		return NULL;
}
int stop_rtp_connection(struct ice_rtp_connection *rtp_connection)
{
	if(!rtp_connection)
			return S_OK;
	if(rtp_connection->reading_thread)
	{
		rtp_connection->stopping = 1;
		LogD("join it's reading thread.");
		pthread_join(rtp_connection->reading_thread,NULL);
		LogD("join success");
		rtp_connection->reading_thread = 0;
	}
	return S_OK;
}
int destroy_rtp_connection(struct ice_rtp_connection *rtp_connection)
{
	if(!rtp_connection)
		return S_OK;
	LogD("destroying rtp_connection");
	if(rtp_connection->bridged_rtp)
	{
		LogD("unlike it's bridge.");
		rtp_connection->bridged_rtp->bridged_rtp = NULL;
	}
	if(rtp_connection->reading_thread)
	{
		rtp_connection->stopping = 1;
		LogD("join it's reading thread.");
		pthread_join(rtp_connection->reading_thread,NULL);
		rtp_connection->reading_thread = 0;
		LogD("join success");
	}

	if(rtp_connection->fd)
	{
		LogD("close it's socket fd.");
		close(rtp_connection->fd);
	}
	chkndfree(rtp_connection);
	return S_OK;
}
int resolv_ip(char *server,int port,struct sockaddr_in *address)
{
	struct hostent *server_ent;
	if(!(server_ent = gethostbyname(server))){
		Log("ERROR, no such ice_rtp_server=%s\n",server);
		return S_ERROR;
	}
	address->sin_family = AF_INET;
	address->sin_port = HTONS(port);
	bcopy((char *)server_ent->h_addr, (char *)&(address->sin_addr),server_ent->h_length);
	return S_OK;
}


